<?php

namespace CommonBundle\Controller;

use CommonBundle\DependencyInjection\WechatService;
use CommonBundle\Entity\User;
use CommonBundle\Entity\UserProfile;
use CommonBundle\Service\UserProfileService;
use CommonBundle\Service\UserService;
use CommonBundle\Utils\UUID;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class SecurityController extends RestController
{
    /**
     * Login method
     *
     * @param Request $request
     * @param UserInterface $user
     * @param string $firewall
     */
    private function fastLogin(Request $request, UserInterface $user, $firewall = 'main')
    {
        // Handle getting or creating the user entity likely with a posted form
        // The third parameter "main" can change according to the name of your firewall in security.yml
        $token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
        $this->get('security.token_storage')->setToken($token);

        // If the firewall name is not main, then the set value would be instead:
        // $this->get('session')->set('_security_XXXFIREWALLNAMEXXX', serialize($token));
        $this->get('session')->set("_security_$firewall", serialize($token));

        // Fire the login event manually
        $event = new InteractiveLoginEvent($request, $token);
        $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
    }

    /**
     * Sample:
     *  {"username":"rin","password":"123456"}
     *
     * @Route("/api-login", name="api-login", methods={"POST"})
     * @SWG\Response(
     *     response=200,
     *     description="Account api login",
     *     @SWG\Schema(type="object")
     * )
     * @SWG\Parameter(
     *     name="body",
     *     in="body",
     *     description="Json Content",
     *     type="json",
     *     required=false,
     *     @SWG\Schema(type="object")
     * )
     * @SWG\Tag(name="auth")
     *
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function loginAction(Request $request)
    {
        // init
        $em = $this->getDoctrine()->getManager();
        $userRep = $em->getRepository(User::class);

        try {
            // receive parameters
            $content = json_decode($request->getContent());
            $username = $content->username;
            $planPassword = $content->password;

            /** @var User $user */
            $user = $userRep->findOneByUsername($username);
            if(empty($user)) {
                return $this->Warning('User is not found or invalid password.');
            }

            # encode password
            $encoderService = $this->container->get('security.password_encoder');

            if($encoderService->isPasswordValid($user, $planPassword)) {
                return $this->Success($user->getToken());
            }
            else {
                return $this->Warning('User is not found or invalid password.');
            }
        }
        catch (\Exception $exception) {
            return $this->Warning('User is not found or invalid password.', -1);
        }
    }

    /**
     * Wechat login and register
     *
     * @Route("/wechat/official/login", name="wechat-official-login", methods={"GET"})
     * @SWG\Response(
     *     response=200,
     *     description="Wechat login and register",
     * )
     * @SWG\Parameter(name="code", in="query", type="string")
     * @SWG\Parameter(name="state", in="query", type="string")
     * @SWG\Tag(name="auth")
     *
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function wechatOfficialLoginAction(Request $request)
    {
        // Init
        $userServ = $this->get(UserService::class);
        $userProfileServ = $this->get(UserProfileService::class);

        // Get wechat service for receive oauth2.
        $wechatServ = $this->get(WechatService::class);
        $app = $wechatServ->official();

        $wechatUser = $app->oauth->user();

        // Login only unionid permission
        if ($wechatUser && ($openid = $wechatUser->getId())) {
            try {
                // Find duplication users.
                /** @var User $user */
                $user = $userServ->get(['wechatOpenId' => $openid]);

                // No user, return .
                if (empty($user)) {
                    return $this->Success([
                        'openid' => $openid,
                        'nickname' => $wechatUser->getNickname(),
                        'avatar' => $wechatUser->getAvatar(),
                    ]);
                }
                else {
                    return $this->Success([
                        'token' => $user->getToken(),
                        'nickname' => $wechatUser->getNickname(),
                        'avatar' => $wechatUser->getAvatar(),
                    ]);
                }
            } catch (\Exception $exception) {
                return $this->Warning($exception->getMessage());
            }

        } else {
            return $this->Warning('No permission to login.', Response::HTTP_UNAUTHORIZED);
        }
    }


    /**
     * Wechat generate auth url
     *
     * @Route("/wechat/official/generate-auth-url", name="wechat-generate-auth-url", methods={"GET"})
     * @SWG\Response(
     *     response=200,
     *     description="Wechat generate auth url",
     * )
     * @SWG\Parameter(name="redirect_url", in="query", type="string")
     * @SWG\Tag(name="auth")
     *
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function wechatOfficialGenerateAuthURLAction(Request $request)
    {
        $wechatServ = $this->get(WechatService::class);

        $app = $wechatServ->official();

        $redirectUrl = $request->query->get('redirect_url');
        $url = $app->oauth->redirect($redirectUrl);
        $targetUrl = $url->getTargetUrl();

        return $this->Success($targetUrl);
    }

    /**
     * @Route("/wechat/mini/login", name="wechat-mini-program-login", methods={"GET"})
     * @SWG\Response(
     *     response=200,
     *     description="Wechat mini program Login",
     * )
     * @SWG\Parameter(name="code", in="query", type="string")
     * @SWG\Tag(name="auth")
     *
     * @param Request $request
     * @return Response
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function wechatMiniProgramLoginAction(Request $request)
    {
        // Init
        $wechat = $this->get(WechatService::class);
        $app = $wechat->miniProgram();

        $em = $this->getDoctrine()->getManager();
        $userRep = $this->getDoctrine()->getRepository('CommonBundle:User');
        $return = [];

        $logger = $this->get('logger');
        $logger->info('Weixin Login start...');

        // get code
        $code = $request->query->get('code');
        if(empty($code)) {
            return $this->Warning('Code cannot be null', -1);
        }

        // request from wx server
        $data = $app->auth->session($code);

        if( empty($data) || array_key_exists('errcode', $data)) {
            return $this->Warning($data['errmsg'], $data['errcode']);
        }

        $openId = $data['openid'];

        if(array_key_exists('unionid', $data)) {
            $unionId = $data['unionid'];

            // find duplication users.
            $user = $userRep->findOneByWechatUnionId($unionId);
        }
        if( empty($user) ) {
            // find duplication users.
            $user = $userRep->findOneByWechatOpenId($openId);
        }

        if( empty($user) ) {
            // register a new user.
            $password = 'EJee*li:e5th'.time(); // default password.

            $user = new User();
            $user->setUsername(UUID::v4());

            # encode password
            $factory = $this->get('security.encoder_factory');
            $encoder = $factory->getEncoder($user);
            $password = $encoder->encodePassword($password, $user->getSalt());
            $user->setPassword($password);
            $user->setEmail(time().'@dummy.com');
            $user->setEnabled(true);
            $user->setToken(UUID::v4());
            $em->persist($user);

            // auto create profile
            $profile = new UserProfile();
            $profile->setUser($user);

            $em->persist($profile);

            $addition_message = "SUCCESS";
        }
        else {
            $addition_message = "User already registered, login only.";
        }

        $user->setWechatOpenId($openId);
        $em->flush();

        $return['token'] = $user->getToken();
        return $this->Success($return, $addition_message);
    }
}
